package core

import (
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"io"
	"os"
	"time"
)

const zapConfKey = "zap"

type Zap struct {
	Level         string `mapstructure:"level"`
	Format        string `mapstructure:"format"` //json 或者console
	UseFileWriter bool   `mapstructure:"use-file-writer"`
	UseConsole    bool   `mapstructure:"use-console"`
}

// Logger 初始化zap
func (z *Zap) load() *zap.Logger {

	var cores []zapcore.Core
	if z.UseConsole {
		cores = append(cores, BuildConsoleZapCore())
	}
	if z.UseFileWriter {
		cores = append(cores, BuildFileZapCore(z.Level, z.Format, GetFileWriter()))
	}

	defaultZap = zap.New(zapcore.NewTee(cores...), zap.AddCaller())
	return defaultZap
}

var defaultZap *zap.Logger

func GetLogger() *zap.Logger {
	if defaultZap != nil {
		return defaultZap
	}
	var (
		conf Zap
		err  error
	)
	//获取配置
	if conf, err = Conf[Zap](zapConfKey); err != nil {
		panic(err)
	}
	defaultZap = conf.load()
	return defaultZap
}

// BuildConsoleZapCore 构建控制台输出流
func BuildConsoleZapCore() zapcore.Core {
	return zapcore.NewCore(Encoder("console"), zapcore.Lock(os.Stdout), zap.DebugLevel)
}

// Encoder 构建编码方式
func Encoder(format string) (encoder zapcore.Encoder) {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime) //修改时间显示方式
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder               //日导显示大写级别
	encoderConfig.EncodeCaller = zapcore.FullCallerEncoder                //调用方法全路径
	encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder         //执行时间，以秒为单位
	switch format {
	case "console":
		encoder = zapcore.NewConsoleEncoder(encoderConfig)
	case "json":
		encoder = zapcore.NewJSONEncoder(encoderConfig)
	}
	return
}

// BuildLogLevel 构建日志级别
func BuildLogLevel(level string) (logLevel *zapcore.Level) {
	logLevel = new(zapcore.Level)
	//设置为配置中的日志级别
	err := logLevel.UnmarshalText([]byte(level))
	if err != nil {
		panic(fmt.Errorf("日志级别设置异常: err:%s \n", err))
	}
	return logLevel
}

// BuildFileZapCore 构建默认文件流
func BuildFileZapCore(level, format string, fileWrite io.Writer) zapcore.Core {
	return zapcore.NewCore(Encoder(format), zapcore.AddSync(fileWrite), BuildLogLevel(level))
}
